package com.quick.develop.flowable.framework.convert.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.quick.develop.flowable.constant.BpmConstants;
import com.quick.develop.flowable.domain.dto.task.BpmCommentDTO;
import com.quick.develop.flowable.domain.dto.task.BpmTaskDTO;
import com.quick.develop.flowable.domain.task.BpmTaskDonePageItemRespVO;
import com.quick.develop.flowable.domain.task.BpmTaskRespVO;
import com.quick.develop.flowable.domain.task.BpmTaskTodoItemExternalRespVO;
import com.quick.develop.flowable.domain.task.BpmTaskTodoPageItemRespVO;
import com.quick.develop.flowable.domain.vo.system.SysUserDeptVO;
import com.quick.develop.flowable.utils.BpmTaskUtil;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.mapstruct.*;
import org.mapstruct.factory.Mappers;

import java.util.*;

import static com.quick.develop.framework.utils.collection.CollectionUtils.convertList;
import static com.quick.develop.framework.utils.collection.CollectionUtils.findFirst;

/**
 * @author junyuan.liu
 * @description Bpm 任务 Convert
 * @date 2022/7/6 10:28
 */

@Mapper
public interface BpmTaskConvert {

    BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class);

    default List<BpmTaskTodoPageItemRespVO> convertList1(List<Task> tasks,
                                                         Map<String, ProcessInstance> processInstanceMap,
                                                         Map<String, ProcessDefinition> processDefinitionMap,
                                                         List<SysUserDeptVO> userDeptList ) {
        return convertList(tasks, task -> {
            BpmTaskTodoPageItemRespVO respVO = convert1(task);
            ProcessInstance instance = processInstanceMap.get(task.getProcessInstanceId());
            Map<String, Object> variables = task.getProcessVariables();
            if (variables.containsKey(BpmConstants.INITIATOR_NAME)) {
                respVO.setStartUser(variables.get(BpmConstants.INITIATOR_NAME).toString());
                Optional.ofNullable(getStartUserDept(userDeptList, instance)).ifPresent(respVO::setStartUserDept);
            }
            if (variables.containsKey(BpmConstants.PROCESS_NAME)) {
                respVO.setProcessName(variables.get(BpmConstants.PROCESS_NAME).toString());
            }

            copyTo(instance, respVO);
            ProcessDefinition processDefinition = processDefinitionMap.get(task.getProcessDefinitionId());
            Optional.ofNullable(processDefinition).ifPresent(p -> respVO.setProcessCategory(p.getCategory()));
            Object formCode = variables.get(BpmConstants.BUSINESS_CODE);
            if (Objects.nonNull(formCode)){
                respVO.setFormKey(formCode.toString());
            }
            return respVO;
        });
    }
    
    default String getStartUserDept(List<SysUserDeptVO> userDeptList, ProcessInstance instance) {
        if (CollUtil.isEmpty(userDeptList) || Objects.isNull(instance)) {
            return null;
        }
        SysUserDeptVO vo = findFirst(userDeptList, e ->
                Optional.ofNullable(e)
                        .map(o -> Objects.equals(o.getUserCode(), instance.getStartUserId()))
                        .orElse(false)
        );
        
        return Optional.ofNullable(vo).map(SysUserDeptVO::getDeptName).orElse(null);
    }

    @Mappings({
            @Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState")
            , @Mapping(source = "id", target = "taskId")
    })
    BpmTaskTodoPageItemRespVO convert1(Task bean);

    @Mappings({
            @Mapping(source = "from.name", target = "to.processName")
            , @Mapping(target = "name", ignore = true)
            , @Mapping(target = "description", ignore = true)
    })
    void copyTo(ProcessInstance from, @MappingTarget BpmTaskTodoPageItemRespVO to);

    @Mappings({
            @Mapping(source = "from.name", target = "to.processName")
            , @Mapping(target = "name", ignore = true)
            , @Mapping(target = "description", ignore = true)
    })
    void copyTo1(ProcessInstance from, @MappingTarget BpmTaskTodoItemExternalRespVO to);

    @Named("convertSuspendedToSuspensionState")
    default Integer convertSuspendedToSuspensionState(boolean suspended) {
        return suspended ? SuspensionState.SUSPENDED.getStateCode() :
                SuspensionState.ACTIVE.getStateCode();
    }

    /*@Mappings({
            @Mapping(source = "processInstance.id", target = "id"),
            @Mapping(source = "processInstance.name", target = "name"),
            @Mapping(source = "processInstance.startUserId", target = "startUserId"),
            @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"),
    })*/
    BpmTaskTodoPageItemRespVO convert(ProcessInstance processInstance);

    default List<BpmTaskDonePageItemRespVO> convertList2(List<HistoricTaskInstance> tasks,
                                                         Map<String, HistoricProcessInstance> historicProcessInstanceMap,
                                                         Map<String, ProcessDefinition> processDefinitionMap,
                                                         Map<String, List<Comment>> comments) {
        return convertList(tasks, task -> {
            BpmTaskDonePageItemRespVO respVO = convert2(task);
            HistoricProcessInstance hiInst = historicProcessInstanceMap.get(task.getProcessInstanceId());
            Map<String, Object> variables = task.getProcessVariables();
            if (variables.containsKey(BpmConstants.INITIATOR_NAME)) {
                respVO.setStartUser(variables.get(BpmConstants.INITIATOR_NAME).toString());
            }
            if (variables.containsKey(BpmConstants.PROCESS_NAME)) {
                respVO.setProcessName(variables.get(BpmConstants.PROCESS_NAME).toString());
            }
            copyTo2(hiInst, respVO);

//            ProcessDefinition processDefinition = processDefinitionMap.get(task.getProcessDefinitionId());
//            Optional.ofNullable(processDefinition).ifPresent(p -> respVO.setProcessCategory(p.getCategory()));
            Object formCode = variables.get(BpmConstants.BUSINESS_CODE);
            if (Objects.nonNull(formCode)){
                respVO.setFormKey(formCode.toString());
            }
            List<Comment> list = comments.get(task.getId());
            if (CollUtil.isNotEmpty(list)) {
                respVO.setResult(list.get(0).getType());
                respVO.setComment(list.get(0).getFullMessage());
            }


            return respVO;
        });
    }
    default List<BpmTaskDonePageItemRespVO> convertList2(List<HistoricTaskInstance> tasks) {
        return convertList(tasks, task -> {
            BpmTaskDonePageItemRespVO respVO = convert22(task);
            Map<String, Object> variables = task.getProcessVariables();
            Object formCode = variables.get(BpmConstants.BUSINESS_CODE);
            if (Objects.nonNull(formCode)){
                respVO.setFormKey(formCode.toString());
            }

            if (variables.containsKey(BpmConstants.PROCESS_NAME)) {
                respVO.setProcessName(variables.get(BpmConstants.PROCESS_NAME).toString());
            }
            return respVO;
        });
    }

    @Mappings({
            @Mapping(target = "startTime", ignore = true)
            , @Mapping(source = "id", target = "taskId")
            , @Mapping(source = "endTime", target = "taskEndTime")
            , @Mapping(source = "durationInMillis", target = "taskDurationInMillis")
            , @Mapping(source = "scopeId", target = "businessKey")
            , @Mapping(source = "taskDefinitionId", target = "processDefinitionKey")
            , @Mapping(source = "subScopeId", target = "processCategory")
            , @Mapping(source = "scopeType", target = "result")
            , @Mapping(source = "description", target = "startUser", qualifiedByName = "convertStartUser")
            , @Mapping(source = "description", target = "comment", qualifiedByName = "convertComment")
    })
    BpmTaskDonePageItemRespVO convert22(HistoricTaskInstance bean);

    @Named("convertProcessName")
    default String convertProcessName(String description) {
        return BpmTaskUtil.getSpDesc(description, BpmConstants.PROCESS_NAME_SP);
    }
    @Named("convertStartUser")
    default String convertStartUser(String description) {
        return BpmTaskUtil.getSpDesc(description, BpmConstants.START_USER_SP);
    }
    @Named("convertComment")
    default String convertComment(String description) {
        return BpmTaskUtil.getSpDesc(description, BpmConstants.COMMENT_SP);
    }

    @Mappings({
            @Mapping(source = "from.name", target = "to.processName")
            , @Mapping(source = "from.endTime", target = "to.processEndTime")
            , @Mapping(target = "name", ignore = true)
            , @Mapping(source = "durationInMillis", target = "to.processDurationInMillis")

    })
    void copyTo2(HistoricProcessInstance from, @MappingTarget BpmTaskDonePageItemRespVO to);

    @Mappings({
            @Mapping(target = "startTime", ignore = true)
            , @Mapping(source = "id", target = "taskId")
            , @Mapping(source = "endTime", target = "taskEndTime")
            , @Mapping(source = "durationInMillis", target = "taskDurationInMillis")
    })
    BpmTaskDonePageItemRespVO convert2(HistoricTaskInstance bean);


    default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks,
                                             Map<String, List<Comment>> comments) {
        return convertList(tasks, task -> {
            List<Comment> list = comments.get(task.getId());
            BpmTaskRespVO respVO = convert3(task);
            if (Objects.nonNull(task.getEndTime()) && CollUtil.isNotEmpty(list)) {
                respVO.setResult(list.get(0).getType());
            }
            Optional.ofNullable(list)
                    .ifPresent(sp ->
                            respVO.setCommentList(convert3(sp))
                    );
            return respVO;
        });
    }

    @Mappings({
            @Mapping(target = "startTime", ignore = true)
            , @Mapping(source = "id", target = "taskId")
            , @Mapping(source = "endTime", target = "taskEndTime")
            , @Mapping(source = "durationInMillis", target = "taskDurationInMillis")
    })
    BpmTaskRespVO convert3(HistoricTaskInstance bean);

    List<BpmCommentDTO> convert3(List<Comment> list);

    BpmCommentDTO convert3(Comment co);

    @Mappings({
            @Mapping(source = "taskLocalVariables", target = "multiResult", qualifiedByName = "convertMultiResult")
            , @Mapping(source = "id", target = "taskId")
    })
    BpmTaskDTO convert4(HistoricTaskInstance bean);

    @Named("convertMultiResult")
    default String convertMultiResult(Map<String, Object> localVariables) {
        return Optional.ofNullable(localVariables)
                .map(var -> MapUtil.getStr(var, BpmConstants.MULTI_RESULT))
                .orElse(null);
    }

    List<BpmTaskDTO> convert4(List<HistoricTaskInstance> list);


    default List<BpmTaskDTO> convertList4(List<HistoricTaskInstance> tasks,
                                          Map<String, List<Comment>> comments,
                                          Map<String, String> names) {
        return convertList(tasks, task -> {
            List<Comment> list = comments.get(task.getId());
            BpmTaskDTO taskDto = convert4(task);
            if (Objects.nonNull(task.getEndTime()) && CollUtil.isNotEmpty(list)) {
                taskDto.setResult(list.get(0).getType());
            }
            Optional.ofNullable(list).ifPresent(sp -> taskDto.setCommentList(convert3(sp)));
            Optional.ofNullable(names).ifPresent(m -> taskDto.setAssigneeName(m.get(task.getAssignee())));

            return taskDto;
        });
    }


    default List<BpmTaskDTO> convertList5(List<Task> list, Map<String, String> users) {
        return convertList(list, task -> {
            BpmTaskDTO dto = convert5(task);
            dto.setAssigneeName(MapUtil.getStr(users, task.getAssignee()));
            return dto;
        });
    }

    @Mappings({
            @Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState")
            , @Mapping(source = "id", target = "taskId")
    })
    BpmTaskDTO convert5(Task bean);

    List<BpmTaskDTO> convertList5(List<Task> list);

    @Mappings({
            @Mapping(source = "suspended", target = "suspensionState", qualifiedByName = "convertSuspendedToSuspensionState")
            , @Mapping(source = "id", target = "taskId")
    })
    BpmTaskTodoItemExternalRespVO convert6(Task bean);

    default String paramJoin(String key, String value) {
        return key + "=" + value;
    }


}


